You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
8.7 KiB
244 lines
8.7 KiB
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { useRouter } from "next/navigation";
|
|
import { useEffect, useMemo, useState } from "react";
|
|
import {
|
|
countCompletedDetailedAnswers,
|
|
detailedSections,
|
|
type DetailedSection,
|
|
getDetailedQuestionCount,
|
|
getDetailedSectionProgress,
|
|
getDetailedSectionStorageKey,
|
|
} from "@/lib/detailed-questions";
|
|
|
|
function BackIcon() {
|
|
return (
|
|
<svg
|
|
aria-hidden="true"
|
|
fill="none"
|
|
height="18"
|
|
viewBox="0 0 18 18"
|
|
width="18"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<path
|
|
d="M10.5 4.5 6 9l4.5 4.5"
|
|
stroke="currentColor"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth="1.8"
|
|
/>
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
export default function DetailSectionClient({
|
|
section,
|
|
}: {
|
|
section: DetailedSection;
|
|
}) {
|
|
const [answers, setAnswers] = useState<Record<string, string>>({});
|
|
const [isHydrated, setIsHydrated] = useState(false);
|
|
const router = useRouter();
|
|
|
|
useEffect(() => {
|
|
const savedAnswers = window.localStorage.getItem(
|
|
getDetailedSectionStorageKey(section.id),
|
|
);
|
|
|
|
setAnswers(
|
|
savedAnswers ? (JSON.parse(savedAnswers) as Record<string, string>) : {},
|
|
);
|
|
setIsHydrated(true);
|
|
}, [section.id]);
|
|
|
|
useEffect(() => {
|
|
if (!isHydrated) {
|
|
return;
|
|
}
|
|
|
|
window.localStorage.setItem(
|
|
getDetailedSectionStorageKey(section.id),
|
|
JSON.stringify(answers),
|
|
);
|
|
|
|
const completedTotal = detailedSections.reduce((total, currentSection) => {
|
|
const sectionAnswers =
|
|
currentSection.id === section.id
|
|
? answers
|
|
: ((JSON.parse(
|
|
window.localStorage.getItem(
|
|
getDetailedSectionStorageKey(currentSection.id),
|
|
) ?? "{}",
|
|
) as Record<string, string>) ?? {});
|
|
|
|
return (
|
|
total + countCompletedDetailedAnswers(currentSection, sectionAnswers)
|
|
);
|
|
}, 0);
|
|
|
|
if (completedTotal === getDetailedQuestionCount()) {
|
|
router.replace("/details/complete");
|
|
}
|
|
}, [answers, isHydrated, router, section, section.id]);
|
|
|
|
const completedCount = useMemo(
|
|
() => countCompletedDetailedAnswers(section, answers),
|
|
[answers, section],
|
|
);
|
|
const progress = useMemo(
|
|
() => getDetailedSectionProgress(section, answers),
|
|
[answers, section],
|
|
);
|
|
|
|
const handleChange = (questionId: string, value: string) => {
|
|
setAnswers((currentAnswers) => ({
|
|
...currentAnswers,
|
|
[questionId]: value,
|
|
}));
|
|
};
|
|
|
|
return (
|
|
<main className="flex min-h-screen items-center justify-center bg-[#444446] p-2 sm:p-4">
|
|
<section className="relative aspect-[375/813] w-full max-w-[375px] overflow-hidden rounded-[24px] bg-[#FCF8F7] shadow-[0_30px_70px_rgba(0,0,0,0.34)]">
|
|
<div className="absolute inset-0 bg-[radial-gradient(circle_at_top,#FFB8CE_0%,rgba(255,184,206,0.26)_12%,rgba(255,255,255,0)_34%),linear-gradient(180deg,#FFFDFD_0%,#F9F4F6_100%)]" />
|
|
|
|
<div className="relative z-10 flex h-full flex-col px-4 pb-5 pt-5">
|
|
<div className="flex items-center justify-between px-2 text-[#1C1C1E]">
|
|
<span className="text-[14px] font-semibold">9:41</span>
|
|
<div className="flex items-center gap-1.5">
|
|
<span className="block h-[7px] w-[18px] rounded-[3px] border border-current opacity-85" />
|
|
<span className="block h-[7px] w-[7px] rounded-full bg-current opacity-85" />
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-4 flex items-center justify-between">
|
|
<Link
|
|
aria-label="Back to detailed questions"
|
|
className="flex h-11 w-11 items-center justify-center rounded-full bg-white text-[#202020] shadow-[0_10px_24px_rgba(0,0,0,0.08)]"
|
|
href="/details"
|
|
>
|
|
<BackIcon />
|
|
</Link>
|
|
|
|
<h1
|
|
className="text-[20px] font-semibold text-[#2A1D1E]"
|
|
style={{ fontFamily: "Georgia, Times New Roman, serif" }}
|
|
>
|
|
{section.title}
|
|
</h1>
|
|
|
|
<span className="w-11 text-right text-[12px] font-semibold text-[#6E656B]">
|
|
{progress}%
|
|
</span>
|
|
</div>
|
|
|
|
<div className="mt-6 rounded-[26px] bg-white/90 px-5 py-6 shadow-[0_18px_40px_rgba(0,0,0,0.07)]">
|
|
<p className="text-[12px] font-semibold uppercase tracking-[0.22em] text-[#F05A93]">
|
|
Section Progress
|
|
</p>
|
|
<h2
|
|
className="mt-3 text-[27px] font-semibold leading-[1.15] text-[#2E2327]"
|
|
style={{ fontFamily: "Georgia, Times New Roman, serif" }}
|
|
>
|
|
{section.title}
|
|
</h2>
|
|
<p className="mt-3 text-[14px] leading-6 text-[#675E64]">
|
|
{section.description}
|
|
</p>
|
|
|
|
<div className="mt-5 h-[10px] overflow-hidden rounded-full bg-[#F7D9E4]">
|
|
<div
|
|
className="h-full rounded-full bg-linear-to-r from-[#F04C99] to-[#FF8575] transition-[width] duration-300"
|
|
style={{ width: `${progress}%` }}
|
|
/>
|
|
</div>
|
|
|
|
<p className="mt-3 text-[12px] font-medium tracking-[0.04em] text-[#8B8086]">
|
|
{completedCount} of {section.questions.length} questions answered
|
|
</p>
|
|
</div>
|
|
|
|
<div className="mt-5 flex-1 space-y-4 overflow-y-auto pb-2">
|
|
{section.questions.map((question) => {
|
|
const value = answers[question.id] ?? "";
|
|
|
|
return (
|
|
<article
|
|
className="rounded-[22px] bg-white/92 px-5 py-5 shadow-[0_14px_34px_rgba(0,0,0,0.06)]"
|
|
key={question.id}
|
|
>
|
|
<h3 className="text-[17px] font-semibold leading-7 text-[#31252A]">
|
|
{question.label}
|
|
</h3>
|
|
<p className="mt-2 text-[13px] leading-6 text-[#72686E]">
|
|
{question.description}
|
|
</p>
|
|
|
|
<div className="mt-4">
|
|
{question.type === "textarea" ? (
|
|
<textarea
|
|
className="min-h-[132px] w-full resize-none rounded-[18px] border border-[#F3DFE7] bg-[#FFFDFE] px-4 py-3 text-[15px] leading-7 text-[#33292D] outline-none transition focus:border-[#F05A93]"
|
|
onChange={(event) =>
|
|
handleChange(question.id, event.target.value)
|
|
}
|
|
placeholder={question.placeholder}
|
|
value={value}
|
|
/>
|
|
) : null}
|
|
|
|
{question.type === "text" ? (
|
|
<input
|
|
className="h-[54px] w-full rounded-[18px] border border-[#F3DFE7] bg-[#FFFDFE] px-4 text-[15px] text-[#33292D] outline-none transition focus:border-[#F05A93]"
|
|
onChange={(event) =>
|
|
handleChange(question.id, event.target.value)
|
|
}
|
|
placeholder={question.placeholder}
|
|
type="text"
|
|
value={value}
|
|
/>
|
|
) : null}
|
|
|
|
{question.type === "number" ? (
|
|
<input
|
|
className="h-[54px] w-full rounded-[18px] border border-[#F3DFE7] bg-[#FFFDFE] px-4 text-[15px] text-[#33292D] outline-none transition focus:border-[#F05A93]"
|
|
inputMode="numeric"
|
|
onChange={(event) =>
|
|
handleChange(question.id, event.target.value)
|
|
}
|
|
placeholder={question.placeholder}
|
|
type="number"
|
|
value={value}
|
|
/>
|
|
) : null}
|
|
|
|
{question.type === "date" ? (
|
|
<input
|
|
className="h-[54px] w-full rounded-[18px] border border-[#F3DFE7] bg-[#FFFDFE] px-4 text-[15px] text-[#33292D] outline-none transition focus:border-[#F05A93]"
|
|
onChange={(event) =>
|
|
handleChange(question.id, event.target.value)
|
|
}
|
|
type="date"
|
|
value={value}
|
|
/>
|
|
) : null}
|
|
</div>
|
|
</article>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
<div className="mt-4">
|
|
<Link
|
|
className="flex h-[48px] items-center justify-center rounded-[14px] bg-[#242424] text-[18px] font-semibold text-white shadow-[0_14px_30px_rgba(0,0,0,0.18)]"
|
|
href="/details"
|
|
>
|
|
Back To List
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
);
|
|
}
|